package org.moonlight.jvm.instructions.control.switchcase;

import org.moonlight.jvm.instructions.base.BranchInstruction;
import org.moonlight.jvm.instructions.base.BytecodeReader;
import org.moonlight.jvm.rtda.exclusive.Frame;

/**
 * Java语言中的switch-case语句有两种实现方式：如果case值可以编码成一个索引表，则实现成 table switch 指令；否则实现成 lookup switch 指令。
 * 下面这个 switch 可以编码成 table switch 指令
 *   switch (i) {
 *     case 0: return 0;
 *     case 1: return 1;
 *     case 2: return 2;
 *     default: return -1;
 *   }
 * 下面这个 switch 则会编码成 lookup switch 指令
 *   switch (i) {
 *     case -100: return -1;
 *     case 0: return 0;
 *     case 100: return 1;
 *     default: return -1;
 *   }
 *
 * lookup switch: 1. 先从操作数栈中弹出一个int变量，
 *                2. 然后用它查找matchOffsets，看是否能找到匹配的key。
 *                3. 如果能，则按照value给出的偏移量跳转，
 *                4. 否则按照defaultOffset跳转。
 *                matchOffsets 是一个用数组模拟的Map，如果 key 的索引下标为 i, 那么 val 的索引下标就为 i + 1
 *
 * @author Moonlight
 * @createTime 2023/8/23 9:21
 **/
public class LookupSwitch extends BranchInstruction {

    /** default 语句 跳转偏移量 **/
    private int defaultOffset;
    /** pairs case 键值对的数量 **/
    private int pairs;
    /** 各种 case 情况的偏移量 - 有点像Map，它的 key 是 case值，value是跳转偏移量。 **/
    private int[] matchOffset;

    @Override
    public void fetchOperands(BytecodeReader reader) {
        // tableswitch指令操作码的后面有0～3字节的padding，以保证defaultOffset在字节码中的地址是4的倍数
        reader.skipPadding();

        this.defaultOffset = reader.readInt32();
        this.pairs = reader.readInt32();
        this.matchOffset = reader.readInt32s(this.pairs * 2);
    }

    @Override
    public void execute(Frame frame) {
        int key = frame.getOperandStack().popInt();
        for (int i = 0; i < this.pairs * 2; i += 2) {
            if (this.matchOffset[i] == key) {
                branch(frame, this.matchOffset[i + 1]);
                return;
            }
        }
        branch(frame, this.defaultOffset);
    }

}
